package com.ac.scum.log.web.model.wechat.pay.service;

import com.ac.common.core.exception.BaseException;
import com.ac.common.core.exception.config.ExceptionType;
import com.ac.scum.log.bean.Package;
import com.ac.scum.log.web.model.pack.mapper.PackageMapper;
import com.ac.scum.log.web.model.wechat.pay.mapper.PayPackMapper;
import com.ac.scum.log.web.model.wechat.pay.mapper.PayPackSubMapper;
import com.ac.scum.log.web.model.wechat.pay.mapper.TradeMapper;
import com.ac.scum.pay.PayPack;
import com.ac.scum.pay.PayPackSub;
import com.ac.scum.pay.Trade;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import jakarta.annotation.PostConstruct;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@AllArgsConstructor
public class PayService {

    public static String merchantId = "1562267421";
    public static String appId = "wx0258d652f71cb80c";
    public static String privateKeyPath = "/wechat/pay/apiclient_key.pem";
    public static String localPrivateKeyPath = "/apiclient_key.pem";
    public static String notifyUrl = "https://api.scum-log.cn/wechat/pay/notify";
    public static String merchantSerialNumber = "2C564B9705BDACF9E39F177F50C102B2C88B946D";
    public static String apiV3Key = "zjiisW5eSXnazG1y8BGdNbrxkMSWNbbS";

    private final TradeMapper tradeMapper;
    private final PayPackSubMapper payPackSubMapper;
    private final PackageMapper packageMapper;
    private final PayPackMapper payPackMapper;
    private final StringRedisTemplate stringRedisTemplate;

    public void copyFile(String srcPath, String destPath) {
        Path localPrivateKey = Paths.get(destPath);
        if (!Files.exists(localPrivateKey)) {
            InputStream resourceStream = getClass().getResourceAsStream(srcPath);
            if (resourceStream != null) {
                try (OutputStream outputStream = Files.newOutputStream(localPrivateKey, StandardOpenOption.CREATE)) {
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = resourceStream.read(buffer)) > 0) {
                        outputStream.write(buffer, 0, length);
                    }
                    log.info("文件已成功复制到：" + destPath);
                } catch (IOException e) {
                    log.error("复制文件时发生错误", e);
                }
            } else {
                log.error("无法从类路径加载资源文件 {}", srcPath);
            }
        } else {
            log.info("文件 " + destPath + " 已存在，无需复制。");
        }
    }

    @PostConstruct
    public void initPem() {
        copyFile(privateKeyPath, localPrivateKeyPath);
    }


    public Trade newTrade(PayPack payPack, Long serverId) throws BaseException {

        if (payPack.getOnlyOneFlag()) {
            if (tradeMapper.selectCount(Wrappers.<Trade>lambdaQuery().eq(Trade::getPayPackId, payPack.getId()).eq(Trade::getServerId, serverId).eq(Trade::getTradeState, "SUCCESS")) > 0) {
                throw new BaseException(ExceptionType.Parameter_Error.of("当前套餐只可购买一次"));
            }
        }

        Trade trade = new Trade();
        trade.setServerId(serverId);
        trade.setPayPackId(payPack.getId());
        trade.setAmount(payPack.getAmount());
        trade.setCreateTime(LocalDateTime.now());
        tradeMapper.insert(trade);
        return trade;
    }

    public Map<String, Object> codePay(PayPack payPack, Long serverId) throws BaseException {
        Trade trade = newTrade(payPack, serverId);
        Config config = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKeyFromPath(localPrivateKeyPath)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key).build();
        NativePayService service = new NativePayService.Builder().config(config).build();
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(payPack.getAmount());
        request.setAmount(amount);
        request.setAppid(appId);
        request.setMchid(merchantId);
        request.setDescription(payPack.getPackageName());
        request.setNotifyUrl(notifyUrl);
        request.setOutTradeNo(trade.getId().toString());
        request.setAttach(serverId.toString());
        PrepayResponse response = service.prepay(request);
        String redisKey = "pay:" + trade.getId() + "-" + trade.getServerId();
        stringRedisTemplate.opsForValue().set(redisKey, "un-pay", 30, TimeUnit.MINUTES);
        return new HashMap<>() {{
            put("queryKey", redisKey);
            put("url", response.getCodeUrl());
        }};
    }

    public void paySuccess(Transaction transaction) {

        Trade trade = tradeMapper.selectById(transaction.getOutTradeNo());
        PayPack payPack = payPackMapper.selectById(trade.getPayPackId());

        trade.setDeviceId("N/A");
        trade.setBankType(transaction.getBankType());
        trade.setCloseTime(LocalDateTime.now());
        trade.setSuccessTime(LocalDateTime.now());
        trade.setAmount(transaction.getAmount().getTotal());
        trade.setTradeState(transaction.getTradeState().name());
        trade.setTradeStateDesc(transaction.getTradeStateDesc());
        trade.setTransactionId(transaction.getTransactionId());
        trade.setTradeType(transaction.getTradeType().name());
        trade.setBankType(transaction.getBankType());

        if (payPack.getOnlyOneFlag()) {
            if (tradeMapper.selectCount(Wrappers.<Trade>lambdaQuery().eq(Trade::getPayPackId, payPack.getId()).eq(Trade::getServerId, trade.getServerId()).eq(Trade::getTradeState, "SUCCESS")) > 0) {
                trade.setDeviceId("已有优惠套餐支付记录，等待人工退款");
                tradeMapper.updateById(trade);
                return;
            }
        }
        tradeMapper.updateById(trade);

        List<PayPackSub> payPackSubs = payPackSubMapper.selectList(Wrappers.lambdaQuery(PayPackSub.class).eq(PayPackSub::getPayPackId, trade.getPayPackId()));
        for (PayPackSub payPackSub : payPackSubs) {
            Package pack = packageMapper.selectByServerIdAndPackageId(trade.getServerId(), payPackSub.getPackageId());
            if (pack != null) {
                pack.setExpirationTime((pack.getExpirationTime().isBefore(LocalDateTime.now()) ? LocalDateTime.now() : pack.getExpirationTime()).plusHours(payPackSub.getHours()));
                packageMapper.updatePackageAsServer(pack);
            } else {
                pack = new Package();
                pack.setServerId(trade.getServerId());
                pack.setExpirationTime(LocalDateTime.now().plusHours(payPackSub.getHours()));
                pack.setId(payPackSub.getPackageId());
                packageMapper.insertPackageAsServer(pack);
            }
        }
        stringRedisTemplate.opsForValue().set("pay:" + transaction.getOutTradeNo() + "-" + transaction.getAttach(), "payed", 30, TimeUnit.MINUTES);


    }
}
